﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Backend;
using PersistenceTier;
using System.Threading;


namespace BusinessTier
{
    public class FlightBooking : IFlightBooking
    {

        public static FlightBooking flightBooking;

        private FlightBooking()
        { 
        
        }

        public static FlightBooking GetFlightBookingObj()
        {
            if (flightBooking == null)
                flightBooking = new FlightBooking();

            return flightBooking;
        }


        //Returns null if no flights were found
        public List<Flight> FlightSearch(Airport departAirp, Airport arrivalAirp, String date, int numberOfPassengers)
        {

            
            BasicGraphImpl graph = new BasicGraphImpl();
   
            FlightPersistance dataBase = new FlightPersistance();
            List<Vertex> route = new List<Vertex>();

            List<Leg> firstLegs = new List<Leg>();
            Airport departureAirport = new Airport(departAirp.Id);

            Monitor.Enter(this);
            firstLegs = dataBase.searchFlightsFrom(departureAirport, date, numberOfPassengers);

            //Check if any flight were found from the departure airport 
            // on the specific date
            if (firstLegs.Count == 0)
            {
                Monitor.Exit(this);
                return null;
            }

            Vertex root = new Vertex(departureAirport.Id);
          
            graph.AddVertex(root);

            foreach (Leg leg in firstLegs)
            {


                //Check if the destination is not already included in the graph
                Vertex firstDestination = graph.FindVertex(leg.ArrivalAirp.Id);       

                if (firstDestination == null)
                {
                    firstDestination = new Vertex(leg.ArrivalAirp.Id);
                    graph.AddVertex(firstDestination);
                }

               // The edge is created only if it doesn't exist,otherwise only a leg is added to the edge
                graph.AddEdge(root, firstDestination,leg);

                List<Leg> secondLegs = new List<Leg>();
                
                //Seaerch for all the flights from the
                // given airport on the same date
                secondLegs = dataBase.searchFlightsFrom(leg.ArrivalAirp, date,numberOfPassengers); 

                foreach (Leg secondLeg in secondLegs)
                {

                    if (secondLeg.ArrivalAirp.Id == root.Name)
                        continue;

                    //Check if the destination is not already included in the graph
                    Vertex secondDestination = graph.FindVertex(secondLeg.ArrivalAirp.Id);

           
                    if (secondDestination == null)
                    {
                        secondDestination = new Vertex(secondLeg.ArrivalAirp.Id);
                        graph.AddVertex(secondDestination);
                    }
                    
                    //Add an edge between the two destinations (in case an edge exists only the leg is added)
                    graph.AddEdge(firstDestination, secondDestination,secondLeg);

                    List<Leg> thirdLegs = new List<Leg>();
                    thirdLegs = dataBase.searchFlightsFrom(secondLeg.ArrivalAirp, date,numberOfPassengers);

                    foreach (Leg thirdLeg in thirdLegs)
                    {

                        if (thirdLeg.ArrivalAirp.Id == root.Name || thirdLeg.ArrivalAirp.Id==secondLeg.ArrivalAirp.Id)
                            continue;

                        Vertex thirdDestination = graph.FindVertex(thirdLeg.ArrivalAirp.Id);
                        if (thirdDestination == null)
                        {
                            thirdDestination = new Vertex(thirdLeg.ArrivalAirp.Id);
                            graph.AddVertex(thirdDestination);
                        }
                       
                        graph.AddEdge(secondDestination, thirdDestination, thirdLeg);
                    }

                }

            }

            Monitor.Exit(this);





           


           
         
            List<Flight> flights = new List<Flight>();
            //search(root, arrivalAirp.Id,route);  //buld a possible route from root (depAirport) to the requisted arrivalAirp
          
            bool routeExits = false;
            Dijkstra algorithm = new Dijkstra(graph);
            route = algorithm.getCheapestRoute(root, arrivalAirp.Id);
  
            //Check if there is a possible route
            if (route!=null)
            {
                routeExits = true;
               
            }
            

            while (routeExits)
            {
                Flight theFlight = new Flight();

                
                for (int i = route.Count - 1; i >= 1; i--)
                {

                    Leg flightLeg = route[i].getLegWithLowestPrice(route[i - 1]);
    
                    theFlight.AddLeg(flightLeg);
                    

                    //Remove the leg from the edge between the two vertices
                    route[i].removeLeg(route[i - 1], flightLeg); 
                }

                flights.Add(theFlight);

                route.Clear();
                graph.UnMark();

                route = algorithm.getCheapestRoute(root, arrivalAirp.Id); 
               
                if (route==null)
                {
                    routeExits = false;
                }

      
                
            }
          

            return flights;
           
        }

    
        //POST: Number of seats reduced by the number of passengers.
          // Ticket saved in DB. Passengers added to the DB. Ticket refference returned to customer.
        public String Book (List<int> legsID,List<Passenger> passengers)
        {
          

   
            Flight flight = new Flight();

            foreach (int ID in legsID)
            {
                Leg leg = new Leg(ID);
                flight.AddLeg(leg);

            }

            Monitor.Enter(this);
         TicketPersistence ticketPrst = new TicketPersistence();

        
      
         //Assign database IDs to all passengers objects
         foreach (Passenger pass in passengers)
         {

             pass.DbID = GetMax.getMaxPassId() + passengers.IndexOf(pass) + 1;
         }
            
         Ticket ticket = new Ticket(flight, passengers);

         bool isSuccessful=ticketPrst.Book(ticket);



         Monitor.Exit(this);

       

         if (isSuccessful)
         {

             return ticket.TicketReff;
         }

         else
             return "";
        }

        public String SayHelloTest()
        {
            DateTime dt1, dt2;
            TimeSpan t;

            dt1 = DateTime.Now;// take the current time before entering

            Monitor.Enter(this);
            int id = Thread.CurrentThread.ManagedThreadId;



            dt2 = DateTime.Now; // take the current time just before finishing
            t = dt2 - dt1;
            Monitor.Exit(this);
            return "The thread id was: " + id + "\n" + "Time spent in the method: " + t.TotalSeconds + " seconds";
           
       
        }
    }
}
